#lang racket
(require "note.rkt")
(require "image.rkt")
(require "animation.rkt")
(require rsound)
(provide monde%)

;monde.rkt
;version 1.0.0
(define monde%
  (class object%
    ;----------------------------------- Attributs ----------------------------;
    (define TICKS-PAR-SECONDE 48)
    (init-field (partition (list)))
    (init-field (temps-attente TICKS-PAR-SECONDE))
    (init-field (images (list)))
    (init-field (animations (list)))
    (init-field (ps (make-pstream)))
    (init-field (fini? #f))
    
    ;----------------------------------- Getters ------------------------------;
    (define/public (get-ticks-par-seconde) TICKS-PAR-SECONDE)
    (define/public (get-images) images)
    (define/public (get-animations) animations)
    (define/public (get-partition) partition)
    (define/public (get-ps) ps)
    
    ;----------------------------------- Methodes -----------------------------;
    ;partition-vide?
    ;retourne #t si la partition est vide
    (define/public (partition-vide?)
      (null? partition))
    
    ;afficher
    ;affiche une description du monde dans la console
    (define/public (afficher)
      (printf "monde : \n Partition -> ~a \n Images -> ~a \n Animations -> ~a
               \n Temps-attente -> ~a \n Fini? -> ~a \n ps -> ~a"
              partition 
              (for/list ([i (in-list images)]) (send i getImage))
              (for/list ([a (in-list animations)]) (send a toString)) 
              temps-attente fini? ps))
    
    ;lire-partition
    ;lit le contenu de la partition au temps courant, et modifie le monde
    ;en conséquence.
    (define/public (lire-partition)
      (cond ((and (= temps-attente 0) (partition-vide?)) (fermer-animation))
            ((= temps-attente 0) (lire-note))
            (else (begin (set! temps-attente (- temps-attente 1))
                         (jouer-animations)))
            ))
    
    ;lire-note
    ;lit la première note de la partition
    (define/public (lire-note)
      (define note-courante (car partition))
      (define images-eligibles   ;On définit la liste des images qui attendent d'être animées et qui sont du bon type
        (if (equal? 'null (send note-courante get-image-class));Sa la note n'est pas liée à une image, aucune ne sera elligible
            '()
            (filter (λ (img) (and
                              (not (send img isAnimated?))
                              (equal? (send img getClass) (send note-courante get-image-class))))
                    images)))
      (if (not (null? images-eligibles))         ;SI la liste d'images disponibles n'est pas vide, on lie l'animation désirée
          (begin                                 ;à une image tirée au hasard dans cette liste.
            (bind-animation-to-image
             (list-ref images-eligibles (random (length images-eligibles)))
             (send note-courante get-animation-class)
             (deduire-temps-attente (send note-courante get-type)))
            (pstream-play ps (send note-courante get-son)) ;puis on joue le son
            (jouer-animations);et les animations
            (set! partition (cdr partition))
            (set! temps-attente (deduire-temps-attente (send note-courante get-type))))
          (begin                                  ;SINON on ne fait que jouer le son et les animations
            (pstream-play ps (send note-courante get-son))
            (jouer-animations)
            (set! partition (cdr partition))
            (set! temps-attente (deduire-temps-attente (send note-courante get-type))))))
    
    ;jouer-animations
    ;joue la liste des animations
    (define/public (jouer-animations)
      ;ETAPE 1 - Epuration de la liste des animation (suppression des animations finies)
      (set! animations (filter (λ (anim) (not(send anim isFinished?))) animations))
      ;ETAPE 2 - Lancement des animations vivantes
      (for ([animation-courante (in-list animations)])
        (printf "Anim courante : ~a \n" animation-courante)
        (send animation-courante jouer)))
    
    ;bind-animation-to-image
    ;Lie une nouvelle animation à une image existante en fonction d'une animation-type puis ajoute l'animation
    ;à la liste des animations présentes dans le monde
    (define/public (bind-animation-to-image mon-image anim-class ma-duree)
      (printf "ANIM - class : ~a \n" anim-class)
      (cond
        [(equal? anim-class 'Animation-changer-couleur%) (begin
                                                           (printf "DING\n")
                                                           (define nouvelle-animation (new Animation-changer-couleur% 
                                                                                           (duree ma-duree) 
                                                                                           (rest ma-duree)
                                                                                           (img mon-image)))
                                                           (set! animations (cons nouvelle-animation animations)))]
        [else (printf "DONG\n")]))
    ;(case anim-class
    ;  (("Animation-changer-couleur%") (begin
    ;                                  (printf "changement de couleur !\n")
    ;                                   (define nouvelle-animation (new Animation-changer-couleur% (img mon-image)))
    ;                                 (set! animations (cons nouvelle-animation animations))))
    ;   (else (printf "BOUM\n"))))
    ;(define nouvelle-animation (new (eval anim-class))) ;premier candidat pour le champ "animation-type" : 'animation%
    ;(set! animations (cons nouvelle-animation animations))
    ;mon-image)
    
    ;deduire-temps-attente
    ;Déduis le temps d'attente depuis un type de note
    (define/public (deduire-temps-attente type)
      (case type
        [("noire") TICKS-PAR-SECONDE]
        [("noire-pointee") (* TICKS-PAR-SECONDE 1.5)]
        [("croche") (/ TICKS-PAR-SECONDE 2)]
        [("croche3") (/ TICKS-PAR-SECONDE 3)]
        [("croche4") (/ TICKS-PAR-SECONDE 4)]
        [else TICKS-PAR-SECONDE]))
    
    ;fermer-animation
    ;méthode privée chargée de déclencher le processus de fin du monde
    (define/private (fermer-animation)
      (set! fini? #t))
    
    (super-new)))